package edu.sju.chenshu.system.service.exam.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import edu.sju.chenshu.common.core.constants.Constants;
import edu.sju.chenshu.common.core.domain.Result;
import edu.sju.chenshu.common.core.domain.TableDataInfo;
import edu.sju.chenshu.common.core.enums.ResultCode;
import edu.sju.chenshu.common.security.exception.ServiceException;
import edu.sju.chenshu.system.domain.exam.Exam;
import edu.sju.chenshu.system.domain.exam.ExamQuestion;
import edu.sju.chenshu.system.domain.exam.dto.ExamAddDTO;
import edu.sju.chenshu.system.domain.exam.dto.ExamEditDTO;
import edu.sju.chenshu.system.domain.exam.dto.ExamQueryDTO;
import edu.sju.chenshu.system.domain.exam.dto.ExamQuestionAddDTO;
import edu.sju.chenshu.system.domain.exam.vo.ExamDetailVO;
import edu.sju.chenshu.system.domain.exam.vo.ExamVO;
import edu.sju.chenshu.system.domain.question.Question;
import edu.sju.chenshu.system.domain.question.vo.QuestionVO;
import edu.sju.chenshu.system.manager.ExamCacheManager;
import edu.sju.chenshu.system.mapper.exam.ExamMapper;
import edu.sju.chenshu.system.mapper.exam.ExamQuestionMapper;
import edu.sju.chenshu.system.mapper.question.QuestionMapper;
import edu.sju.chenshu.system.service.exam.IExamService;
import cn.hutool.core.bean.BeanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public TableDataInfo list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        List<ExamVO> examVOList = examMapper.selectExamList(examQueryDTO);
        return getTableDataInfo(examVOList);
    }

    @Override
    public Result<String> add(ExamAddDTO examAddDTO) {
        checkExamSaveParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);
        examMapper.insert(exam);
        return Result.ok(exam.getExamId().toString());
    }

    @Override
    public Result<Void> questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        //判断竞赛是否为空
        Exam exam = getExam(examQuestionAddDTO.getExamId());
        checkExam(exam);
        //竞赛已发布不允许添加题目
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISHED);
        }

        Set<Long> questionIds = examQuestionAddDTO.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIds)){
            return Result.ok();
        }
        //判断要添加的题目是否有数据为空
        //优化1: 批量查询
        List<Question> questionList = questionMapper.selectBatchIds(questionIds);
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIds.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
        }

        return toResult(saveExamQuestion(questionIds, exam));
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();
        //验证竞赛是否存在
        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam, examDetailVO);
        //从竞赛-题目表中查询出QuestionId字段,并且赋值给集合中的ExamQuestion对象
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        //如果集合为空,则直接返回前面处理好的examDetailVO
        if (CollectionUtil.isEmpty(examQuestionList)) {
            return examDetailVO;
        }
        //走到此处证明竞赛有对应题目，将ExamQuestion中的questionId提取出来,然后进行in查询
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));
        //走到这里就将竞赛中的题目列表查询出来了
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList, QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public Result<Void> edit(ExamEditDTO examEditDTO) {
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        Exam exam = getExam(examEditDTO.getExamId());
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISHED);
        }
        checkExam(exam);
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return toResult(examMapper.updateById(exam));
    }

    @Override
    public Result<Void> questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISHED);
        }
        checkExam(exam);
        return toResult(examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId)));
    }

    @Override
    public Result<Void> delete(Long examId) {
        //检查竞赛是否存在或已开赛
        Exam exam = getExam(examId);
        checkExam(exam);
        //删除竞赛的题目
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        return toResult(examMapper.deleteById(examId));
    }

    @Override
    public Result<Void> publish(Long examId) {
        //判断竞赛是否存在
        Exam exam = getExam(examId);
        //检查竞赛是否已结束
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISHED);
        }
        //判断是否有题目
        //select count(0) from tb_exam_question where exam_id = #{examId}
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if(count == null || count <= 0){
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.TRUE);

        //将发布的竞赛加到redis缓存结构中
        examCacheManager.addCache(exam);
        return toResult(examMapper.updateById(exam));
    }

    @Override
    public Result<Void> cancelPublish(Long examId) {
        //判断竞赛是否存在
        Exam exam = getExam(examId);
        //判断是否已开赛
        checkExam(exam);
        //检查竞赛是否已结束
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISHED);
        }
        exam.setStatus(Constants.FALSE);

        examCacheManager.deleteCache(examId);

        return toResult(examMapper.updateById(exam));
    }

    private boolean saveExamQuestion(Set<Long> questionIds, Exam exam) {
        int num = 1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIds) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
        }
        //优化2: 批量插入数据
        return saveBatch(examQuestionList);
    }

    //对标题是否重复进行判断
    private void checkExamSaveParams(ExamAddDTO examSaveDTO, Long examId) {
        List<Exam> examList =
                examMapper.selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId!=null,Exam::getExamId, examId));
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    //判断竞赛是否开始
    private static void checkExam(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    //获取竞赛
    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXIST);
        }
        return exam;
    }
}
